/*--- 触摸插件 ---*/
(function(w,d){
	//定义 touch 构造函数(使用安全模式，不使用 this 关键字)
	var touch = function(selector){
		return new touch.fn.init(selector);	
	},
	document = d;
	
	touch.fn = touch.prototype = {
		constructor:touch,
		//所有的事件类型
		events:[
			'touchstart','touchend',
			'swipeleft','swiperight','swipeup','swipedown',
			'drag','dragvertical','draghorizontal',
			'tap','longtap'
		],
		init:function(selector){
			/*
				@note 初始元素属性都在这里, 使用 elm 来存储元素
			*/
			//1. 元素 (css 选择符或者 dom 元素)
			this.elm = (typeof selector == 'string') ? document.querySelector(selector) : selector;
			//2. 每个元素的事件库
			this._events = {};
			//3. 运行方向(默认为 false, 0,1 对应水平和垂直)
			this._dir = false;
			//4. longTap 的定时器
			this._timer = null;
			//5. 触摸状态
			this._isTouched = false;
			//6. 长触摸的时间
			this._longTapTime = 500;
			//事件绑定
			this.elm !== null && this.touch();
		}
		//事件绑定
		,on:function(evt,hdr){
			/*
				@note evt 事件类型数组, hdr 事件函数 
			*/
			var evtArr = evt.split(' '),//不存在分隔符时，返回一项数组
				i = 0,
				len = evtArr.length,
				isFunc = typeof hdr == 'function',
				type;
			for(;i < len;i++){
				type = evtArr[i];
				this._events[type]	= this._events[type] || [];
				if(isFunc){
					this._events[type].push(hdr);
				}
			}
			return this;
		}
		//移除事件
		,remove:function(evt,handler){
			/*
				@note evt 事件类型数组
			*/
			var evtArr = evt.split(' '),//不存在分隔符时，返回一项数组
				i = 0,
				len = evtArr.length,
				type;
			for(;i < len;i++){
				type = evtArr[i];
				//如果存在事件
				if(this._events[type] instanceof Array){
					//如果存在指定的函数
					if(handler){
						var handlers = this._events[type];
						//查找指定函数的位置
						for(var i = 0,len = handlers.length;i < len;i++){
							if(handler === handlers[i]){
								break;
							}
						}
						//删除指定函数
						handlers.splice(i,1);
					}else{
						//清空
						this._events[type] = [];
					}
				}
			}
			return this;
		}
		//事件触发
		,trigger:function(evt,e){
			/*
				@param 
					evt this._events.xxx 具体事件
					e 事件对象
					s touchmove 传入的额外状态参数
			*/
			//存在事件类型未被赋值过 undefined, 同时 drag 水平和垂直如果在没有发生的时，也是 undefined 传入的 bool
			if(!!evt){
				for(var i = 0,len = evt.length;i < len;i++){
					evt[i].call(this,e);
				}
			}
		}
		//touch 进行事件绑定
		,touch:function(){
			var _t = this,
				sx,sy,
				disX,disY;
			//触摸开始事件
			this.elm.addEventListener('touchstart',function(e){
				/*
					@note 触发的事件: touchStart,longTap
				*/
				//只跟踪一次触摸
				if(e.touches.length !== 1){
					return;
				}
				//阻止默认行为， click 事件会在低版本安卓系统失效，改用 tap, 同时组织滚动，能触发低端 安卓的 touchmove
				e.preventDefault();
				_t._dir = false;
				_t._isTouched = true;
				_t._isLongTap = false;
				_t._sTime = Date.now();
				var status = {
					startX:sx
					,startY:sy
				};
				sx = e.touches[0].clientX,
				sy = e.touches[0].clientY;
				//触摸开始 
				_t.trigger(_t._events.touchstart,{
					originalEvent:e,
					type:'touchstart',
					status:status,
					target:_t.elm	
				});
				_t._timer = setTimeout(function(){
					//如果为假，后面的不执行, 执行条件是 触摸未移动且手指未离开
					(_t._isTouched && _t._dir === false) && (_t.trigger(_t._events.longtap, {
						originalEvent:e,
						type:'longtap',
						status:status,
						target:_t.elm
					}),_t._isLongTap = true);
				},_t._longTapTime);
			});
			//触摸滑动
			this.elm.addEventListener('touchmove',function(e){
				//只跟踪一次触摸
				if(e.touches.length !== 1){
					return;
				}
				var mx = e.touches[0].clientX,
					my = e.touches[0].clientY,
					x,y;
				disX = mx - sx;
				disY = my - sy;
				x = Math.abs(disX);
				y = Math.abs(disY);
				//状态对象
				var status = {
					startX:sx
					,startY:sy
					,moveX:mx
					,moveY:my
					,disX:disX
					,disY:disY
				};
				//水平 - 允许 4px 的偏移，这样比较合理，用户触摸后手指稍微的抖动不触发 touchmove 关联的 swip 事件
				//必须是第一次移动,也就是只触发第一次的移动行为，比如如果先向左，再向右认为是左滑动，不会去处理右滑动
				if(x !== 0 && x >= 4 && x > y && _t._dir === false){
					_t._dir = 0;
				}
				//垂直 
				if(y !==0 && y >= 4 && y > x && _t._dir === false){
					_t._dir = 1;
				}
				//拖拽 确定是拖拽行为
				_t._dir !== false && _t.trigger(_t._events.drag,{
					originalEvent:e,
					type:'drag',
					status:status,
					target:_t.elm
				});
				//水平和垂直拖拽
				_t.trigger(_t._events[['draghorizontal','dragvertical'][_t._dir]],{
					originalEvent:e,
					type:['draghorizontal','dragvertical'][_t._dir],
					status:status,
					target:_t.elm
				});
			});
			//触摸结束
			this.elm.addEventListener('touchend',function(e){
				//只跟踪一次触摸
				if(e.touches.length === 0 && e.changedTouches.length !== 1){
					return;
				}
				var ex = e.changedTouches[0].clientX,
					ey = e.changedTouches[0].clientY,
					x,y;
				disX = ex - sx;
				disY = ey - sy;
				x = Math.abs(disX);
				y = Math.abs(disY);
				//状态对象
				var status = {
					startX:sx
					,startY:sy
					,moveX:ex
					,moveY:ey
					,disX:disX
					,disY:disY
				};
				//通过这个值已经确定定时器的内容不会执行，但是还是要清除一下定时器，免得占用资源
				_t._isTouched = false;
				//运行的时间
				_t._disTime = Date.now() - _t._sTime;
				//清除  longTap 的定时器
				clearTimeout(_t._timer);
				if(_t._disTime < 150){
					//左右滑动
					if(_t._dir === 0){
						disX < 0 && _t.trigger(_t._events.swipeleft,{
							originalEvent:e,
							type:'swipeleft',
							target:_t.elm
						});
						disX > 0 && _t.trigger(_t._events.swiperight,{
							originalEvent:e,
							type:'swiperight',
							target:_t.elm
						});
					}
					//上下滑动
					if(_t._dir === 1){
						disY < 0 && _t.trigger(_t._events.swipeup,{
							originalEvent:e,
							type:'swipeup',
							target:_t.elm
						});
						disY > 0 && _t.trigger(_t._events.swipedown,{
							originalEvent:e,
							type:'swipedown',
							target:_t.elm
						});
					}
					//轻击
					_t._dir === false && _t.trigger(_t._events.tap,{
						originalEvent:e,
						type:'tap',
						target:_t.elm
					});
				}else{
					//tap 150 与 longTap 500 之间的时间，以及 
					(_t._dir !== false || _t._dir === false && _t._isLongTap === false) && _t.trigger(_t._events.touchend,{
						originalEvent:e,
						type:'touchend',
						status:status,
						target:_t.elm
					});
				}
			});
		}
	};
	//init 方法原型回指 touh 原型，保证方法通过 touch.fn.init 实例化的实例能访问 touch 的原型方法
	touch.fn.init.prototype = touch.fn;
	//将 touch 设为 window 的属性，方便全局引用
	window.touch = touch;
})(window,document);